import abc
import os
import re
import asyncio

__all__ = ["NameSearch", "ContentSearch"]

search_dict = {}


def name_search(root, key):
    for dir_, _, files in root:
        if key in dir_:
            yield 0, dir_
        for file in files:
            if key in file:
                yield 1, os.path.join(dir_, file)


class Search(abc.ABC):

    def __init__(self, root, key):
        self.root = os.walk(root)
        self.key = key

    @abc.abstractmethod
    def run(self):
        pass

    @abc.abstractmethod
    def read(self, *args):
        pass

    @abc.abstractmethod
    def hit(self, *args):
        pass


class Hit(abc.ABC):
    """
    搜索结果返回
    """

    def __init__(self, g_run):
        self.g = g_run

    @abc.abstractmethod
    def show(self):
        pass


class Reader(abc.ABC):

    def __init__(self, path, prog):
        self.path = path
        self.prog = prog

    @abc.abstractmethod
    def read(self):
        pass


class NameSearch(Search):

    def __init__(self, root, key):
        super(NameSearch, self).__init__(root, key)

    def run(self):
        hit_pre = name_search(self.root, self.key)
        self.hit(hit_pre)

    def read(self):
        pass

    def hit(self, hit_pre):
        h = NameHitter(hit_pre)
        h.show()
        del h


class ContentSearch(Search):

    def __init__(self, root, key):
        super(ContentSearch, self).__init__(root, key)
        self.prog = re.compile(key)
        self.suf2Reader = {".c": TextReader,
                           ".csv": TextReader,
                           ".java": TextReader,
                           ".py": TextReader,
                           ".txt": TextReader}
        self.Reader2Hit = {TextReader: TextHit}

    def run(self):
        print("\nline\tloc\tpath\n")
        loop = asyncio.get_event_loop()
        tasks = []
        for dir_, _, files in self.root:
            for file in files:
                suffix = os.path.splitext(file)[1]
                reader_ = self.suf2Reader.get(suffix, 0)
                if reader_:
                    path = f"{dir_}\\{file}"
                    tasks.append(self.read(path, reader_))
        loop.run_until_complete(asyncio.wait(tasks))
        loop.close()


    async def read(self, path, reader_):
        r = reader_(path, self.prog)
        line_locs = await r.read()
        if line_locs:
            self.hit(self.Reader2Hit[reader_], (path, line_locs))

    def hit(self, Hitter, hit_pre):
        h = Hitter(hit_pre)
        h.show()
        del h


class TextReader(Reader):

    def __init__(self, path, prog):
        super(TextReader, self).__init__(path, prog)

    async def read(self):
        try:
            with open(self.path, "r", encoding="utf-8") as fp:
                lines = fp.readlines()
            result = []
            for i, line in enumerate(lines, 1):
                k = [m.start() for m in re.finditer(self.prog, line)]
                if k:
                    result += [(i, loc) for loc in k]
            return result
        except ValueError:
            pass


class NameHitter(Hit):

    def __init__(self, g_run):
        super(NameHitter, self).__init__(g_run)

    def show(self):
        print("\nis_file\tab_path\n")
        for info in self.g:
            print("{}\t\t\t{}".format(*info))


class TextHit(Hit):

    def __init__(self, g_run):
        super(TextHit, self).__init__(g_run)


    def show(self):
        path, line_locs = self.g
        for line, loc in line_locs:
            print(f"{line}\t{loc}\t{path}")
